import java.io.*;
import java.util.*;
import java.net.*;

public class PlayerClient {

	private String _addr;
	private int _port;
	private Socket _sock;
	private BufferedInputStream _bis = null;
	private BufferedOutputStream _bos = null;
	private Random _rand = new Random();

	private int _gsIndx = -1;
	private int _nPlayers = -1;
	private int [] _gameState;
	private int [] _diceRoll = new int[5];
	private Player _player = new Player();
	private boolean _verbose = false;

	//precalculated statistics
	private float [][] _numberRoll_1;
	private float [][] _numberRoll_2;

	private void loadStats() {
		loadMatrix(_numberRoll_1, "numberRoll_1.txt");
		loadMatrix(_numberRoll_2, "numberRoll_2.txt");
	}

	private void loadMatrix(float [][] mat, String filename) {
		try {
		BufferedReader in = new BufferedReader(new FileReader(filename));
		String line;
		int row = 0;
		while ( (line = in.readLine()) != null ) {
			if (line.length() == 0) {
				break;
			}
			String [] nums = line.split(" ");
			for (int i=0; i<nums.length; i++) {
				mat[row][i] = Float.valueOf(nums[i]);
			}
			row++;
		}

		System.out.println("\n" + filename);
		for (float [] r : mat) {
			for (float n : r) {
				System.out.print(n + "\t");
			}
			System.out.println();
		}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public PlayerClient(String addr, int port) {
		_addr = addr;
		_port = port;

		_numberRoll_1 = new float[6][6];
		_numberRoll_2 = new float[6][6];
		loadStats();

		try {
			_sock = new Socket(_addr,_port);
			_bos = new BufferedOutputStream(_sock.getOutputStream());
			_bis = new BufferedInputStream(_sock.getInputStream());
			int initSize = 0;
			byte [] init = new byte[8];
			if ( (initSize = _bis.read(init,0,8)) <= 0)  {
				if(_verbose) System.out.println("Failed to initialize.");
				System.exit(0);
			}
			_gsIndx = ByteManipulator.getInt(init,0,false);
			_nPlayers = ByteManipulator.getInt(init,4,false);
			_gameState = new int[_nPlayers * 14];
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public PlayerClient(String addr, int port, boolean verbose) {
		this(addr,port);
		_verbose = verbose;
	}

	public void run() {
		int msgSize = 0;
		byte [] msg = new byte[2048];
		try {
			while ( (msgSize = _bis.read(msg,0,2048)) > 0 ) {
				processMessage(msg,msgSize);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	void printDiceRoll() {
		for (int d : _diceRoll) {
			if(_verbose) System.out.print(d + " ");
		}
		if(_verbose) System.out.println();
	}

	public void processMessage(byte [] msg, int msgSize) throws Exception{

		int index = 0;
		int msgType = ByteManipulator.getInt(msg,0,false); index +=4;
		index = ByteManipulator.getIntArray(msg,_diceRoll,index,0,5,false);

		//if(_verbose) System.out.println("Received a message of type " + msgType);
		int move = -1;
		if (msgType == 0) {
			//_gsIndx = ByteManipulator.getInt(msg,index,false); index+=4;
			index = ByteManipulator.getIntArray(msg,_gameState,index,0,_nPlayers * 14,false);
			
			if(_verbose) System.out.print("\nGame State: ");
			for (int j=0; j<_nPlayers; j++) {
				for (int i=1; i<14; i++) {
					if(_verbose) System.out.print(_gameState[j*14 + i] + " ");
				}
				if(_verbose) System.out.println();
			}

			printDiceRoll();

		} else if (msgType == 1) {
			printDiceRoll();
		} else if (msgType == 2) {
			printDiceRoll();
		} else if (msgType == 99) {
			_bos.close();
			_bis.close();
			System.exit(0);
		} else {
			if(_verbose) System.out.println("derf!");
		}
		move = getMove(msgType); 
		if(_verbose) System.out.println("Sending move: " + move);
		int indx = ByteManipulator.addInt(msg,move,0,false);
		_bos.write(msg,0,4);
		_bos.flush();
	}

	private int getMove(int msgType) {
		//return greedyMove(msgType);

		/*
		 * Random moves:
		 */
		
		GameState gs = new GameState(_gameState, _nPlayers);
		DiceRoll dr = new DiceRoll(_diceRoll);
		if (msgType < 2) {
			return _rand.nextInt(31);
		} else {
			return _rand.nextInt(12) + 1;
		}
		
	}

	int greedyMove(int msgType) {
		GameState gs = new GameState(_gameState, _nPlayers);
		DiceRoll dr = new DiceRoll(_diceRoll);
		int move = 0;
		float tmpScore = -1;
		float maxScore = -1;
		int maxCat = -1;
		if (msgType < 2) {
			//keep the dice for the category you have the best shot of making
			for (int cat : GameState.CATS) {
				if (cat == GameState.CHANCE) {
					continue;
				}
				if (gs.getData(_gsIndx,cat) > 0) {
					continue;
				}
				tmpScore = ((float) _player.scoreRoll(new GameState(), dr, cat, true)) / ((float)cat);
				if (cat == GameState.THREE_OF_A_KIND) tmpScore /= 2;
				if(_verbose) System.out.print(cat + ":" + tmpScore + ", ");
				if (tmpScore >= maxScore) {
					maxScore = tmpScore;
					maxCat = cat;
				}
			}
			if(_verbose) System.out.println();
			
			if (maxCat > 0 && maxCat <= GameState.SIXES) {
				if(_verbose) System.out.println("Going for " + maxCat);
				for (int i=0;i<5;i++) {
					if (dr._dice[i] == maxCat) {
						move += (1 << i);
					}
				}
			} else if (maxCat == GameState.SMALL_STRAIGHT) {
				int [] diceCount = {0,0,0,0,0,0};
				for (int i=0; i<5; i++) {
					if(++diceCount[dr._dice[i]-1] > 1) {
						move = 31 - (1 << i);
						if(_verbose) System.out.println("Going for Small Straight (1)");
					}
				}
				if (diceCount[0] == 1 && diceCount[1] == 0) {
					//1 3 4 5 6
					for (int i=0; i<5; i++) {
						if (dr._dice[i] == 1) {
							move = 31 - (1 << i);
							if(_verbose) System.out.println("Going for Small Straight (2)");
							break;
						}
					}
				} else if (diceCount[5] == 1 && diceCount[4] == 0) {
					//1 2 3 4 6
					for (int i=0; i<5; i++) {
						if (dr._dice[i] == 6) {
							if(_verbose) System.out.println("Going for Small Straight (3)");
							move = 31 - (1 << i);
							break;
						}
					}
				}
			} else if ((maxCat == GameState.LARGE_STRAIGHT || maxCat == GameState.YAHTZEE || maxCat == GameState.FULL_HOUSE)) {
				if (maxScore > 0) {
					if(_verbose) System.out.println("Got a " + GameState.hr(maxCat) + ".  Standing Pat");
					move = 31;
				} else if (maxCat == GameState.FULL_HOUSE) {
					if(_verbose) System.out.println("going for full house");
					int [] diceCount = {0,0,0,0,0,0};
					for (int i=0; i<5; i++) {
						++diceCount[dr._dice[i]-1];
					}
					for (int i=0; i<5; i++) {
						if (diceCount[dr._dice[i]-1] > 1) {
							move += (1 << i);
						}
					}
				}
			} else if (maxCat == GameState.THREE_OF_A_KIND || maxCat == GameState.FOUR_OF_A_KIND) {
				if(_verbose) System.out.println("Got a Three of a Kind");
				int tcat = -1;
				int [] diceCount = {0,0,0,0,0,0};
				for (int i=0; i<5; i++) {
					if(++diceCount[dr._dice[i]-1] == 3) {
						tcat = dr._dice[i];
						break;
					}
				}
				for (int i=0;i<5;i++) {
					if (dr._dice[i] == tcat) {
						move += (1 << i);
					}
				}
			}

		} else {
			if (_player.scoreRoll(new GameState(), dr, GameState.YAHTZEE, true) > 0 && gs.getData(_gsIndx,GameState.YAHTZEE) < 0) {
				return GameState.YAHTZEE;
			} else if (_player.scoreRoll(new GameState(), dr, GameState.FOUR_OF_A_KIND, true) > 0 && gs.getData(_gsIndx,GameState.FOUR_OF_A_KIND) < 0) {
				return GameState.FOUR_OF_A_KIND;
			} else if (_player.scoreRoll(new GameState(), dr, GameState.LARGE_STRAIGHT, true) > 0 && gs.getData(_gsIndx,GameState.LARGE_STRAIGHT) < 0) {
				return GameState.LARGE_STRAIGHT;
			}

			for (int cat : GameState.CATS) {
				if (gs.getData(_gsIndx,cat) > 0) {
					continue;
				}
				tmpScore =  ((float)_player.scoreRoll(new GameState(), dr, cat, true)) / ((float)cat);
				if (tmpScore > maxScore) {
					maxScore = tmpScore;
					maxCat = cat;
				}
			}

			if (maxCat == GameState.THREE_OF_A_KIND && gs.getData(_gsIndx,GameState.FOUR_OF_A_KIND) < 0 && _player.scoreRoll(new GameState(), dr, GameState.FOUR_OF_A_KIND, true) > 0) {
				maxCat = GameState.FOUR_OF_A_KIND;
			} else if (maxCat == GameState.SMALL_STRAIGHT && gs.getData(_gsIndx,GameState.LARGE_STRAIGHT) < 0 && _player.scoreRoll(new GameState(), dr, GameState.LARGE_STRAIGHT, true) > 0) {
				maxCat = GameState.LARGE_STRAIGHT;
			}
			move = maxCat;
		}
		return move;
	}

	public static void main(String [] args) {
		if (args.length < 1) {
			PlayerClient pc = new PlayerClient("127.0.0.1",7000);
			pc.run();
		} else if (args.length == 1) {
			PlayerClient pc = new PlayerClient("127.0.0.1",Integer.valueOf(args[0]));
			pc.run();
		} else if (args.length == 2) {
			PlayerClient pc = new PlayerClient(args[0],Integer.valueOf(args[1]));
			pc.run();
		} else if (args.length == 3) {
			PlayerClient pc = new PlayerClient(args[0],Integer.valueOf(args[1]),true);
			pc.run();
		}
	}

}
